#include "app_uart.h"

#define UART_FRAME_DATA_LEN_MAX (0x104)

enum {
    UART_RX_STATE_IDLE = 0x00,
    UART_RX_STATE_HEAD1,
    UART_RX_STATE_HEAD2,
    UART_RX_STATE_VERSION,
    UART_RX_STATE_COMMAND,
    UART_RX_STATA_DATAL1,
    UART_RX_STATE_DATAL2,
    UART_RX_STATE_DATA,
    UART_RX_STATE_CHECKSUM,
};

typedef struct {
    unsigned char state;
    unsigned char command;
    unsigned char checksum;
    unsigned char data[UART_FRAME_DATA_LEN_MAX];
    unsigned short datalen;
    unsigned short offset;
} app_uart_rx_frame_t;

volatile static app_uart_rx_frame_t rx_frame = {0x00};
volatile static app_uart_cb_t app_uart_cb[APP_UART_MAX_CB] = {NULL};

static inline void app_uart_write_data(const unsigned char *data, unsigned short length)
{
    drv_uart_write_data(UART_USART1, data, length);
}

static unsigned char app_uart_checksum(const unsigned char *data, unsigned short length)
{
    unsigned char checksum = 0x00;
    for (unsigned short i = 0; i < length; i++) {
        checksum += data[i];
    }

    return checksum;
}


static unsigned char app_uart_frame_packet_byte(unsigned char *buffer, unsigned char byte)
{
    *buffer = byte;
    return 0x01;
}

static unsigned short app_uart_frame_packet_data(unsigned char *buffer, const unsigned char *data, unsigned short length)
{
    memcpy(buffer, data, length);
    return length;
}

static unsigned char first_rsp = 0x01;
static void app_uart_heat_beat_query(void)
{
    unsigned char tx[] = {0x55, 0xaa, MCU_TX_VER, HEAT_BEAT_CMD, 0x00, 0x01, 0x01, 0x04};
    if (first_rsp) {
        tx[6] = 0x00;
        tx[7] = 0x03;
        first_rsp = 0x00;
    }
    app_uart_write_data(tx, 0x08);
}

static void app_uart_product_info_query(void)
{
    unsigned char tx[128] = {0x55, 0xaa, MCU_TX_VER, PRODUCT_INFO_CMD, };
    unsigned char offset = LENGTH_HIGH;
    unsigned short length = strlen(STRING_INFO);

    offset += app_uart_frame_packet_byte(tx+offset, (length >> 8) & 0xFF);
    offset += app_uart_frame_packet_byte(tx+offset, length & 0xFF);
    offset += app_uart_frame_packet_data(tx+offset, (const unsigned char *)STRING_INFO, length);

    tx[offset] = app_uart_checksum(tx, offset);
    offset += 1;

    app_uart_write_data(tx, offset);

    app_uart_tx_wifi_reset_noti();
}

static void app_uart_work_mode_query(void)
{
    unsigned char tx[7] = {0x55, 0xaa, MCU_TX_VER, WORK_MODE_CMD, 0x00, 0x00, 0x04};
    app_uart_write_data(tx, 0x07);
}

static void app_uart_wifi_state_notify(const unsigned char *data, unsigned short length)
{
    ret_if_true(NULL == data || 0x01 != length)

    if (app_uart_cb[WIFI_STATE_CB] != NULL) {
        app_uart_cb[WIFI_STATE_CB](data, length);
    }
    unsigned char tx[7] = {0x55, 0xaa, 0x03, 0x03, 0x00, 0x00, 0x05};
    app_uart_write_data(tx, 0x07);
}

static void app_uart_wifi_extend_notify(const unsigned char *data, unsigned short length)
{
    ret_if_true(NULL == data || 0 == length)

    unsigned char tx[16] = {0x55, 0xaa, MCU_TX_VER, WIFI_EXTEND_CMD, 0x00, 0x01};
    unsigned char offset = DATA_START;

    if (data[0] == 0x05 && length == 0x02) {
        // wifi reset notify
        if (app_uart_cb[WIFI_RESET_CB]) {
            app_uart_cb[WIFI_RESET_CB](data, length);
        }
        offset += app_uart_frame_packet_byte(tx+offset, 0x05);
    } else if (data[0] == 0x02 && length == 0x09) {
        // wifi time notify
        if (data[1] == 0x01 && app_uart_cb[LMT_NOTIF_CB]) {
            // local time format
            app_uart_cb[LMT_NOTIF_CB](&data[2], length - 2);
        }
        offset += app_uart_frame_packet_byte(tx+offset, 0x02);
    }

    unsigned char checksum = app_uart_checksum(tx, offset);
    offset += app_uart_frame_packet_byte(tx+offset, checksum);
    app_uart_write_data(tx, offset);
}

static void app_uart_dp_notify_resp(const unsigned char *data, unsigned short length)
{
    if (data[0] == 0x00) {
        // failure
    } else {
        // ok
    }
}

static void app_uart_cmd_handler(unsigned char cmd, const unsigned char *data, unsigned short length)
{
    switch (cmd) {
        case HEAT_BEAT_CMD: {
            app_uart_heat_beat_query();
            break;
        }
        case PRODUCT_INFO_CMD: {
            app_uart_product_info_query();
            if (app_uart_cb[FCTR_WFI_READY_CB]) {
                app_uart_cb[FCTR_WFI_READY_CB](NULL, 0x00);
            }
            break;
        }
        case WORK_MODE_CMD: {
            app_uart_work_mode_query();
            break;
        }
        case WIFI_STATE_CMD: {
            app_uart_wifi_state_notify(data, length);
            break;
        }
        case WIFI_RESET_CMD: {
            break;
        }
        case WIFI_MODE_CMD: {
            break;
        }
        case DATA_QUERT_CMD: {
            if (app_uart_cb[DPID_SET_CB]) {
                app_uart_cb[DPID_SET_CB](data, length);
            }
            break;
        }
        case STATE_UPLOAD_CMD: {
            break;
        }
        case STATE_QUERY_CMD: {
            if (app_uart_cb[DPID_GET_CB]) {
                app_uart_cb[DPID_GET_CB](data, length);
            }
            break;
        }
        case UPDATE_START_CMD: {
            if (app_uart_cb[OTA_NOTIF_CB]) {
                app_uart_cb[OTA_NOTIF_CB](data, length);
            }
            break;
        }
        case UPDATE_TRANS_CMD: {
            if (app_uart_cb[OTA_TRANS_CB]) {
                app_uart_cb[OTA_TRANS_CB](data, length);
            }
            break;
        }
        case FACTORY_MODE_CMD: {
            break;
        }
        case WIFI_TEST_CMD: {
            break;
        }
        case GET_LOCAL_TIME_CMD: {
            if (data[0] == 0x01 && app_uart_cb[LMT_NOTIF_CB]) {
                app_uart_cb[LMT_NOTIF_CB](&data[1], length - 1);
            }
            break;
        }
        case STATE_UPLOAD_SYN_CMD: {
            break;
        }
        case STATE_UPLOAD_SYN_RECV_CMD: {
            break;
        }
        case HEAT_BEAT_STOP: {
            break;
        }
        case GET_WIFI_STATUS_CMD: {
            break;
        }
        case WIFI_CONNECT_TEST_CMD: {
            if (app_uart_cb[FCTR_WFI_CONN_CB]) {
                app_uart_cb[FCTR_WFI_CONN_CB](data, length);
            }
            break;
        }
        case WIFI_RSSI_CMD: {
            if (app_uart_cb[FCTR_WFI_RSI_CB]) {
                app_uart_cb[FCTR_WFI_RSI_CB](data, length);
            }
            break;
        }

        case WIFI_EXTEND_CMD: {
            app_uart_wifi_extend_notify(data, length);
            break;
        }
        default:
            break;
    }
}

static void app_uart_rx_handler(unsigned char dat)
{
    switch (rx_frame.state) {
        case UART_RX_STATE_IDLE: {
            if (dat == FRAME_FIRST) {
                rx_frame.state = UART_RX_STATE_HEAD1;
                rx_frame.checksum = dat;
            }
            break;
        }
        case UART_RX_STATE_HEAD1: {
            if (dat == FRAME_SECOND) {
                rx_frame.state = UART_RX_STATE_HEAD2;
                rx_frame.checksum += dat;
            } else {
                rx_frame.state = UART_RX_STATE_IDLE;
            }
            break;
        }
        case UART_RX_STATE_HEAD2: {
            if (dat == MCU_RX_VER) {
                rx_frame.state = UART_RX_STATE_VERSION;
                rx_frame.checksum += dat;
            } else {
                rx_frame.state = UART_RX_STATE_IDLE;
            }
            break;
        }
        case UART_RX_STATE_VERSION: {
            rx_frame.command = dat;
            rx_frame.checksum += dat;
            rx_frame.state = UART_RX_STATE_COMMAND;
            break;
        }
        case UART_RX_STATE_COMMAND: {
            rx_frame.datalen = dat << 8;
            rx_frame.checksum += dat;
            rx_frame.state = UART_RX_STATA_DATAL1;
            break;
        }
        case UART_RX_STATA_DATAL1: {
            rx_frame.datalen += dat;
            rx_frame.checksum += dat;
            rx_frame.offset = 0;
            if (rx_frame.datalen == 0) {
                rx_frame.state = UART_RX_STATE_DATA;
            } else {
                rx_frame.state = UART_RX_STATE_DATAL2;
            }
            break;
        }
        case UART_RX_STATE_DATAL2: {
            if ((rx_frame.datalen > 0) && (rx_frame.datalen <= UART_FRAME_DATA_LEN_MAX)) {
                rx_frame.data[rx_frame.offset++] = dat;
                rx_frame.checksum += dat;
                if (rx_frame.offset >= rx_frame.datalen) {
                    // rx finish
                    rx_frame.state = UART_RX_STATE_DATA;
                }
            } else {
                rx_frame.state = UART_RX_STATE_IDLE;
            }
            break;
        }
        case UART_RX_STATE_DATA: {
            if (rx_frame.checksum == dat) {
                // checksum ok
                app_uart_cmd_handler(rx_frame.command, (unsigned char *)rx_frame.data, rx_frame.datalen);
                rx_frame.state = UART_RX_STATE_IDLE;
            } else {
                // checksum error
                rx_frame.state = UART_RX_STATE_IDLE;
            }
            break;
        }
        default:
            rx_frame.state = UART_RX_STATE_IDLE;
            break;
    }
}

inline void app_uart_init(void)
{
    drv_uart_init(UART_USART1, 115200);
}

void app_uart_rx_task(void)
{
    while (0x00 == drv_uart_rx_is_empty(UART_USART1)) {
        unsigned char dat = drv_uart_read_byte(UART_USART1);
        app_uart_rx_handler(dat);
    }
}

void app_uart_tx_wifi_reset(void)
{
    unsigned char tx[] = {0x55, 0xaa, MCU_TX_VER, WIFI_RESET_CMD, 0x00, 0x00, 0x06};

    app_uart_write_data(tx, 0x07);
}

void app_uart_tx_wifi_smart_config_reset(void)
{
    unsigned char tx[] = {0x55, 0xaa, MCU_TX_VER, WIFI_MODE_CMD, 0x00, 0x01, 0x00, 0x08};

    app_uart_write_data(tx, 0x08);
}

void app_uart_tx_wifi_distribution_network(void)    //串口配网
{
    unsigned char length = 0;
    unsigned char crc = 0;
    unsigned char i = 0;
    unsigned char temp[100];
    unsigned char tx[100] = {0x55, 0xaa, MCU_TX_VER, DISTRIBUTION_NETWORD_CMD};

    length = strlen(DISTRIBUTION_NETWORD_INFO);
    memcpy(temp, (const unsigned char *)DISTRIBUTION_NETWORD_INFO, length);
    tx[4] = length << 16;
    tx[5] = length;
    for (i = 0; i < length; i++)  tx[i + 6] = temp[i];
    for (i = 0; i < length + 6; i++)  crc += tx[i];
    tx[length + 6] = crc;

    //for (i = 0; i < length + 7; i++)  printf("tx[%d] = 0x%x\n", i, tx[i]);
    app_uart_write_data(tx, length + 7);
}

void app_uart_tx_wifi_state_get(void)
{
    unsigned char tx[] = {0x55, 0xaa, MCU_TX_VER, GET_WIFI_STATUS_CMD, 0x00, 0x00, 0x2d};
    app_uart_write_data(tx, 0x07);
}

void app_uart_tx_wifi_heat_off(void)
{
    unsigned char tx[] = {0x55, 0xaa, MCU_TX_VER, HEAT_BEAT_STOP, 0x00, 0x00, 0x27};
    app_uart_write_data(tx, 0x07);
}

void app_uart_tx_wifi_reset_noti(void)
{
    unsigned char tx[16] = {0x55, 0xaa, MCU_TX_VER, WIFI_EXTEND_CMD, 0x00, 0x01};
    unsigned char offset = DATA_START;

    offset += app_uart_frame_packet_byte(tx+offset, 0x04);
    unsigned char checksum = app_uart_checksum(tx, offset);
    offset += app_uart_frame_packet_byte(tx+offset, checksum);
    app_uart_write_data(tx, offset);
}

void app_uart_tx_wifi_time_noti(void)
{
    unsigned char tx[16] = {0x55, 0xaa, MCU_TX_VER, WIFI_EXTEND_CMD, 0x00, 0x01};
    unsigned char offset = DATA_START;
}

void app_uart_tx_wifi_lmt_noti(void)
{
    unsigned char tx[] = {0x55, 0xaa, MCU_TX_VER, GET_LOCAL_TIME_CMD, 0x00, 0x00, 0x1e};
    app_uart_write_data(tx, 0x07);
}

void app_uart_tx_ota_notif(void)
{
    unsigned char tx[] = {0x55, 0xaa, MCU_TX_VER, UPDATE_START_CMD, 0x00, 0x01, 0x00, 0x0D};
    app_uart_write_data(tx, 0x08);
}

void app_uart_tx_ota_trans(void)
{
    unsigned char tx[] = {0x55, 0xaa, MCU_TX_VER, UPDATE_TRANS_CMD, 0x00, 0x00, 0x0d};
    app_uart_write_data(tx, 0x07);
}

void app_uart_tx_ota_func_set(void)
{
    unsigned char tx[16] = {0x55, 0xaa, MCU_TX_VER, DEV_NEW_FUNC_CMD, 0x00, 0x01};
    unsigned char offset = DATA_START;

    offset += app_uart_frame_packet_byte(tx+offset, 0x00);

    unsigned char str[] = {"{\"mcu_ota\":0\",\"abv\":8}"};
    offset += app_uart_frame_packet_data(tx+offset, str, strlen((const char *)str));
    
    unsigned char checksum = app_uart_checksum(tx, offset);
    offset += app_uart_frame_packet_byte(tx+offset, checksum);
    app_uart_write_data(tx, offset);
}

void app_uart_tx_dp_notify(const unsigned char *data, unsigned short length)
{
    unsigned char tx[256] = {0x55, 0xaa, MCU_TX_VER, STATE_UPLOAD_CMD};
    unsigned short offset = LENGTH_HIGH;

    offset += app_uart_frame_packet_byte(tx+offset, ((length >> 8) & 0xFF));
    offset += app_uart_frame_packet_byte(tx+offset, (length & 0xFF));

    offset += app_uart_frame_packet_data(tx+offset, data, length);
    
    unsigned char checksum = app_uart_checksum(tx, offset);
    offset += app_uart_frame_packet_byte(tx+offset, checksum);
    app_uart_write_data(tx, offset);
}

void app_uart_tx_dp_notify_sync(const unsigned char *data, unsigned short length)
{
    unsigned char tx[150] = {0x55, 0xaa, MCU_TX_VER, STATE_UPLOAD_SYN_CMD};
    unsigned short offset = LENGTH_HIGH;

    offset += app_uart_frame_packet_byte(tx+offset, ((length >> 8) & 0xFF));
    offset += app_uart_frame_packet_byte(tx+offset, (length & 0xFF));

    offset += app_uart_frame_packet_data(tx+offset, data, length);
    
    unsigned char checksum = app_uart_checksum(tx, offset);
    offset += app_uart_frame_packet_byte(tx+offset, checksum);
    app_uart_write_data(tx, offset);
}

//void app_uart_tx_dp_notify_record(const unsigned char *data, unsigned short length)
void app_uart_tx_dp_notify_record(const unsigned char *data, unsigned short length)
{
    unsigned char tx[150] = {0x55, 0xaa, MCU_TX_VER, WIFI_EXTEND_CMD, };
    unsigned short offset = LENGTH_HIGH;

    //unsigned short _length = length + 0x09;
    unsigned short _length = 0x11;

    offset += app_uart_frame_packet_byte(tx+offset, ((_length >> 8) & 0xFF));
    offset += app_uart_frame_packet_byte(tx+offset, (_length & 0xFF));

    offset += app_uart_frame_packet_byte(tx+offset, 0x0B);
    offset += app_uart_frame_packet_byte(tx+offset, 0x01);
    offset += app_uart_frame_packet_byte(tx+offset, 0x01);  //local time

    unsigned char time[6] = {0x00};
    
    time[0] = data[0];
    time[1] = data[1];
    time[2] = data[2];
    time[3] = data[3];
    time[4] = data[4];
    time[5] = 0;  
    offset += app_uart_frame_packet_data(tx+offset, time, sizeof(time));

    offset += app_uart_frame_packet_byte(tx+offset, 0x0E);
    offset += app_uart_frame_packet_byte(tx+offset, 0x02);
    offset += app_uart_frame_packet_byte(tx+offset, 0x00);
    offset += app_uart_frame_packet_byte(tx+offset, 0x04);

    offset += app_uart_frame_packet_byte(tx+offset, 0x00);
    offset += app_uart_frame_packet_byte(tx+offset, 0x00);
    offset += app_uart_frame_packet_byte(tx+offset, 0x00);
    //offset += app_uart_frame_packet_byte(tx+offset, 0x01);
    offset += app_uart_frame_packet_byte(tx+offset, data[5]);

    unsigned char checksum = app_uart_checksum(tx, offset);
    offset += app_uart_frame_packet_byte(tx+offset, checksum);
    app_uart_write_data(tx, offset);
}

void app_uart_tx_scan_router(void)
{
    unsigned char tx[] = {0x55, 0xaa, 0x03, 0x0e, 0x00, 0x00, 0x10};
    app_uart_write_data(tx, 0x07);
}

void app_uart_tx_connect_router(unsigned char *data, unsigned short length)
{
    unsigned char tx[150] = {0x55, 0xaa, MCU_TX_VER, WIFI_CONNECT_TEST_CMD, };
    unsigned short offset = LENGTH_HIGH;

    offset += app_uart_frame_packet_byte(tx+offset, (length >> 8) & 0xFF);
    offset += app_uart_frame_packet_byte(tx+offset, (length & 0xFF));

    if (data && 0 < length) {
        memcpy(tx+offset, data, length);
    }
    offset += length;
    unsigned char checksum = app_uart_checksum(tx, offset);
    offset += app_uart_frame_packet_byte(tx+offset, checksum);
    app_uart_write_data(tx, offset);
}

void app_uart_tx_connect_rssi_get(void)
{
    unsigned char tx[150] = {0x55, 0xaa, MCU_TX_VER, WIFI_RSSI_CMD, 0x00, 0x00};
    unsigned short offset = DATA_START;

    unsigned char checksum = app_uart_checksum(tx, offset);
    offset += app_uart_frame_packet_byte(tx+offset, checksum);
    app_uart_write_data(tx, offset);
}


void app_uart_cb_register(app_uart_cb_type_t type, void (*cb) (const unsigned char *, unsigned short))
{
    ret_if_true(type >= APP_UART_MAX_CB)
    app_uart_cb[type] = cb;
}